{
 "cells": [
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**问题：**\n",
    "假设你正在为一个电影推荐系统设计一个简单的KNN算法。我们有以下一些用户的电影评分数据，数据由两个特征组成：用户对电影A和电影B的评分，分别在1-5之间。用户的标签（电影类型偏好）是动作片（标签0）或者是喜剧片（标签1）。我们有一个新用户，他给电影A评分为3，电影B评分为4。请问这个用户可能偏好哪种类型的电影？\n",
    "\n",
    "**数据：**\n",
    "\n",
    "| 用户   | 电影A评分 | 电影B评分 | 偏好类型 |\n",
    "| ------ | --------- | --------- | -------- |\n",
    "| 用户1  | 5         | 1         | 动作片   |\n",
    "| 用户2  | 4         | 2         | 动作片   |\n",
    "| 用户3  | 2         | 5         | 喜剧片   |\n",
    "| 用户4  | 1         | 4         | 喜剧片   |\n",
    "| 用户5  | 3         | 2         | 动作片   |\n",
    "| 用户6  | 2         | 5         | 喜剧片   |\n",
    "\n",
    "你需要做以下步骤：\n",
    "1. 构造数据\n",
    "2. 创建KNN模型\n",
    "3. 使用数据训练模型\n",
    "4. 预测新用户的喜好"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 0. 引入核心包"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import pandas as pd\n",
    "import matplotlib.pyplot as plt\n",
    "from sklearn.neighbors import KNeighborsClassifier"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "df = pd.read_csv('ratings.csv')\n",
    "X_train = df[['Movie1', 'Movie2']].values\n",
    "y_train = df['Preference'].values"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 1. X, y"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "X = 0\n",
    "\n",
    "y = 1  # 0表示动作片，1表示喜剧片"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 2. 创建 KNN 模型\n",
    "k = 1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "k = 1\n",
    "knn = KNeighborsClassifier(n_neighbors=k)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 3. 训练模型"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<style>#sk-container-id-1 {\n",
       "  /* Definition of color scheme common for light and dark mode */\n",
       "  --sklearn-color-text: black;\n",
       "  --sklearn-color-line: gray;\n",
       "  /* Definition of color scheme for unfitted estimators */\n",
       "  --sklearn-color-unfitted-level-0: #fff5e6;\n",
       "  --sklearn-color-unfitted-level-1: #f6e4d2;\n",
       "  --sklearn-color-unfitted-level-2: #ffe0b3;\n",
       "  --sklearn-color-unfitted-level-3: chocolate;\n",
       "  /* Definition of color scheme for fitted estimators */\n",
       "  --sklearn-color-fitted-level-0: #f0f8ff;\n",
       "  --sklearn-color-fitted-level-1: #d4ebff;\n",
       "  --sklearn-color-fitted-level-2: #b3dbfd;\n",
       "  --sklearn-color-fitted-level-3: cornflowerblue;\n",
       "\n",
       "  /* Specific color for light theme */\n",
       "  --sklearn-color-text-on-default-background: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, black)));\n",
       "  --sklearn-color-background: var(--sg-background-color, var(--theme-background, var(--jp-layout-color0, white)));\n",
       "  --sklearn-color-border-box: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, black)));\n",
       "  --sklearn-color-icon: #696969;\n",
       "\n",
       "  @media (prefers-color-scheme: dark) {\n",
       "    /* Redefinition of color scheme for dark theme */\n",
       "    --sklearn-color-text-on-default-background: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, white)));\n",
       "    --sklearn-color-background: var(--sg-background-color, var(--theme-background, var(--jp-layout-color0, #111)));\n",
       "    --sklearn-color-border-box: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, white)));\n",
       "    --sklearn-color-icon: #878787;\n",
       "  }\n",
       "}\n",
       "\n",
       "#sk-container-id-1 {\n",
       "  color: var(--sklearn-color-text);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 pre {\n",
       "  padding: 0;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 input.sk-hidden--visually {\n",
       "  border: 0;\n",
       "  clip: rect(1px 1px 1px 1px);\n",
       "  clip: rect(1px, 1px, 1px, 1px);\n",
       "  height: 1px;\n",
       "  margin: -1px;\n",
       "  overflow: hidden;\n",
       "  padding: 0;\n",
       "  position: absolute;\n",
       "  width: 1px;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-dashed-wrapped {\n",
       "  border: 1px dashed var(--sklearn-color-line);\n",
       "  margin: 0 0.4em 0.5em 0.4em;\n",
       "  box-sizing: border-box;\n",
       "  padding-bottom: 0.4em;\n",
       "  background-color: var(--sklearn-color-background);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-container {\n",
       "  /* jupyter's `normalize.less` sets `[hidden] { display: none; }`\n",
       "     but bootstrap.min.css set `[hidden] { display: none !important; }`\n",
       "     so we also need the `!important` here to be able to override the\n",
       "     default hidden behavior on the sphinx rendered scikit-learn.org.\n",
       "     See: https://github.com/scikit-learn/scikit-learn/issues/21755 */\n",
       "  display: inline-block !important;\n",
       "  position: relative;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-text-repr-fallback {\n",
       "  display: none;\n",
       "}\n",
       "\n",
       "div.sk-parallel-item,\n",
       "div.sk-serial,\n",
       "div.sk-item {\n",
       "  /* draw centered vertical line to link estimators */\n",
       "  background-image: linear-gradient(var(--sklearn-color-text-on-default-background), var(--sklearn-color-text-on-default-background));\n",
       "  background-size: 2px 100%;\n",
       "  background-repeat: no-repeat;\n",
       "  background-position: center center;\n",
       "}\n",
       "\n",
       "/* Parallel-specific style estimator block */\n",
       "\n",
       "#sk-container-id-1 div.sk-parallel-item::after {\n",
       "  content: \"\";\n",
       "  width: 100%;\n",
       "  border-bottom: 2px solid var(--sklearn-color-text-on-default-background);\n",
       "  flex-grow: 1;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-parallel {\n",
       "  display: flex;\n",
       "  align-items: stretch;\n",
       "  justify-content: center;\n",
       "  background-color: var(--sklearn-color-background);\n",
       "  position: relative;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-parallel-item {\n",
       "  display: flex;\n",
       "  flex-direction: column;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-parallel-item:first-child::after {\n",
       "  align-self: flex-end;\n",
       "  width: 50%;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-parallel-item:last-child::after {\n",
       "  align-self: flex-start;\n",
       "  width: 50%;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-parallel-item:only-child::after {\n",
       "  width: 0;\n",
       "}\n",
       "\n",
       "/* Serial-specific style estimator block */\n",
       "\n",
       "#sk-container-id-1 div.sk-serial {\n",
       "  display: flex;\n",
       "  flex-direction: column;\n",
       "  align-items: center;\n",
       "  background-color: var(--sklearn-color-background);\n",
       "  padding-right: 1em;\n",
       "  padding-left: 1em;\n",
       "}\n",
       "\n",
       "\n",
       "/* Toggleable style: style used for estimator/Pipeline/ColumnTransformer box that is\n",
       "clickable and can be expanded/collapsed.\n",
       "- Pipeline and ColumnTransformer use this feature and define the default style\n",
       "- Estimators will overwrite some part of the style using the `sk-estimator` class\n",
       "*/\n",
       "\n",
       "/* Pipeline and ColumnTransformer style (default) */\n",
       "\n",
       "#sk-container-id-1 div.sk-toggleable {\n",
       "  /* Default theme specific background. It is overwritten whether we have a\n",
       "  specific estimator or a Pipeline/ColumnTransformer */\n",
       "  background-color: var(--sklearn-color-background);\n",
       "}\n",
       "\n",
       "/* Toggleable label */\n",
       "#sk-container-id-1 label.sk-toggleable__label {\n",
       "  cursor: pointer;\n",
       "  display: block;\n",
       "  width: 100%;\n",
       "  margin-bottom: 0;\n",
       "  padding: 0.5em;\n",
       "  box-sizing: border-box;\n",
       "  text-align: center;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 label.sk-toggleable__label-arrow:before {\n",
       "  /* Arrow on the left of the label */\n",
       "  content: \"▸\";\n",
       "  float: left;\n",
       "  margin-right: 0.25em;\n",
       "  color: var(--sklearn-color-icon);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 label.sk-toggleable__label-arrow:hover:before {\n",
       "  color: var(--sklearn-color-text);\n",
       "}\n",
       "\n",
       "/* Toggleable content - dropdown */\n",
       "\n",
       "#sk-container-id-1 div.sk-toggleable__content {\n",
       "  max-height: 0;\n",
       "  max-width: 0;\n",
       "  overflow: hidden;\n",
       "  text-align: left;\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-0);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-toggleable__content.fitted {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-0);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-toggleable__content pre {\n",
       "  margin: 0.2em;\n",
       "  border-radius: 0.25em;\n",
       "  color: var(--sklearn-color-text);\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-0);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-toggleable__content.fitted pre {\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-0);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 input.sk-toggleable__control:checked~div.sk-toggleable__content {\n",
       "  /* Expand drop-down */\n",
       "  max-height: 200px;\n",
       "  max-width: 100%;\n",
       "  overflow: auto;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 input.sk-toggleable__control:checked~label.sk-toggleable__label-arrow:before {\n",
       "  content: \"▾\";\n",
       "}\n",
       "\n",
       "/* Pipeline/ColumnTransformer-specific style */\n",
       "\n",
       "#sk-container-id-1 div.sk-label input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
       "  color: var(--sklearn-color-text);\n",
       "  background-color: var(--sklearn-color-unfitted-level-2);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-label.fitted input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
       "  background-color: var(--sklearn-color-fitted-level-2);\n",
       "}\n",
       "\n",
       "/* Estimator-specific style */\n",
       "\n",
       "/* Colorize estimator box */\n",
       "#sk-container-id-1 div.sk-estimator input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-2);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-estimator.fitted input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-2);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-label label.sk-toggleable__label,\n",
       "#sk-container-id-1 div.sk-label label {\n",
       "  /* The background is the default theme color */\n",
       "  color: var(--sklearn-color-text-on-default-background);\n",
       "}\n",
       "\n",
       "/* On hover, darken the color of the background */\n",
       "#sk-container-id-1 div.sk-label:hover label.sk-toggleable__label {\n",
       "  color: var(--sklearn-color-text);\n",
       "  background-color: var(--sklearn-color-unfitted-level-2);\n",
       "}\n",
       "\n",
       "/* Label box, darken color on hover, fitted */\n",
       "#sk-container-id-1 div.sk-label.fitted:hover label.sk-toggleable__label.fitted {\n",
       "  color: var(--sklearn-color-text);\n",
       "  background-color: var(--sklearn-color-fitted-level-2);\n",
       "}\n",
       "\n",
       "/* Estimator label */\n",
       "\n",
       "#sk-container-id-1 div.sk-label label {\n",
       "  font-family: monospace;\n",
       "  font-weight: bold;\n",
       "  display: inline-block;\n",
       "  line-height: 1.2em;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-label-container {\n",
       "  text-align: center;\n",
       "}\n",
       "\n",
       "/* Estimator-specific */\n",
       "#sk-container-id-1 div.sk-estimator {\n",
       "  font-family: monospace;\n",
       "  border: 1px dotted var(--sklearn-color-border-box);\n",
       "  border-radius: 0.25em;\n",
       "  box-sizing: border-box;\n",
       "  margin-bottom: 0.5em;\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-0);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-estimator.fitted {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-0);\n",
       "}\n",
       "\n",
       "/* on hover */\n",
       "#sk-container-id-1 div.sk-estimator:hover {\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-2);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-estimator.fitted:hover {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-2);\n",
       "}\n",
       "\n",
       "/* Specification for estimator info (e.g. \"i\" and \"?\") */\n",
       "\n",
       "/* Common style for \"i\" and \"?\" */\n",
       "\n",
       ".sk-estimator-doc-link,\n",
       "a:link.sk-estimator-doc-link,\n",
       "a:visited.sk-estimator-doc-link {\n",
       "  float: right;\n",
       "  font-size: smaller;\n",
       "  line-height: 1em;\n",
       "  font-family: monospace;\n",
       "  background-color: var(--sklearn-color-background);\n",
       "  border-radius: 1em;\n",
       "  height: 1em;\n",
       "  width: 1em;\n",
       "  text-decoration: none !important;\n",
       "  margin-left: 1ex;\n",
       "  /* unfitted */\n",
       "  border: var(--sklearn-color-unfitted-level-1) 1pt solid;\n",
       "  color: var(--sklearn-color-unfitted-level-1);\n",
       "}\n",
       "\n",
       ".sk-estimator-doc-link.fitted,\n",
       "a:link.sk-estimator-doc-link.fitted,\n",
       "a:visited.sk-estimator-doc-link.fitted {\n",
       "  /* fitted */\n",
       "  border: var(--sklearn-color-fitted-level-1) 1pt solid;\n",
       "  color: var(--sklearn-color-fitted-level-1);\n",
       "}\n",
       "\n",
       "/* On hover */\n",
       "div.sk-estimator:hover .sk-estimator-doc-link:hover,\n",
       ".sk-estimator-doc-link:hover,\n",
       "div.sk-label-container:hover .sk-estimator-doc-link:hover,\n",
       ".sk-estimator-doc-link:hover {\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-3);\n",
       "  color: var(--sklearn-color-background);\n",
       "  text-decoration: none;\n",
       "}\n",
       "\n",
       "div.sk-estimator.fitted:hover .sk-estimator-doc-link.fitted:hover,\n",
       ".sk-estimator-doc-link.fitted:hover,\n",
       "div.sk-label-container:hover .sk-estimator-doc-link.fitted:hover,\n",
       ".sk-estimator-doc-link.fitted:hover {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-3);\n",
       "  color: var(--sklearn-color-background);\n",
       "  text-decoration: none;\n",
       "}\n",
       "\n",
       "/* Span, style for the box shown on hovering the info icon */\n",
       ".sk-estimator-doc-link span {\n",
       "  display: none;\n",
       "  z-index: 9999;\n",
       "  position: relative;\n",
       "  font-weight: normal;\n",
       "  right: .2ex;\n",
       "  padding: .5ex;\n",
       "  margin: .5ex;\n",
       "  width: min-content;\n",
       "  min-width: 20ex;\n",
       "  max-width: 50ex;\n",
       "  color: var(--sklearn-color-text);\n",
       "  box-shadow: 2pt 2pt 4pt #999;\n",
       "  /* unfitted */\n",
       "  background: var(--sklearn-color-unfitted-level-0);\n",
       "  border: .5pt solid var(--sklearn-color-unfitted-level-3);\n",
       "}\n",
       "\n",
       ".sk-estimator-doc-link.fitted span {\n",
       "  /* fitted */\n",
       "  background: var(--sklearn-color-fitted-level-0);\n",
       "  border: var(--sklearn-color-fitted-level-3);\n",
       "}\n",
       "\n",
       ".sk-estimator-doc-link:hover span {\n",
       "  display: block;\n",
       "}\n",
       "\n",
       "/* \"?\"-specific style due to the `<a>` HTML tag */\n",
       "\n",
       "#sk-container-id-1 a.estimator_doc_link {\n",
       "  float: right;\n",
       "  font-size: 1rem;\n",
       "  line-height: 1em;\n",
       "  font-family: monospace;\n",
       "  background-color: var(--sklearn-color-background);\n",
       "  border-radius: 1rem;\n",
       "  height: 1rem;\n",
       "  width: 1rem;\n",
       "  text-decoration: none;\n",
       "  /* unfitted */\n",
       "  color: var(--sklearn-color-unfitted-level-1);\n",
       "  border: var(--sklearn-color-unfitted-level-1) 1pt solid;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 a.estimator_doc_link.fitted {\n",
       "  /* fitted */\n",
       "  border: var(--sklearn-color-fitted-level-1) 1pt solid;\n",
       "  color: var(--sklearn-color-fitted-level-1);\n",
       "}\n",
       "\n",
       "/* On hover */\n",
       "#sk-container-id-1 a.estimator_doc_link:hover {\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-3);\n",
       "  color: var(--sklearn-color-background);\n",
       "  text-decoration: none;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 a.estimator_doc_link.fitted:hover {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-3);\n",
       "}\n",
       "</style><div id=\"sk-container-id-1\" class=\"sk-top-container\"><div class=\"sk-text-repr-fallback\"><pre>KNeighborsClassifier(n_neighbors=1)</pre><b>In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook. <br />On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.</b></div><div class=\"sk-container\" hidden><div class=\"sk-item\"><div class=\"sk-estimator fitted sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-1\" type=\"checkbox\" checked><label for=\"sk-estimator-id-1\" class=\"sk-toggleable__label fitted sk-toggleable__label-arrow fitted\">&nbsp;&nbsp;KNeighborsClassifier<a class=\"sk-estimator-doc-link fitted\" rel=\"noreferrer\" target=\"_blank\" href=\"https://scikit-learn.org/1.5/modules/generated/sklearn.neighbors.KNeighborsClassifier.html\">?<span>Documentation for KNeighborsClassifier</span></a><span class=\"sk-estimator-doc-link fitted\">i<span>Fitted</span></span></label><div class=\"sk-toggleable__content fitted\"><pre>KNeighborsClassifier(n_neighbors=1)</pre></div> </div></div></div></div>"
      ],
      "text/plain": [
       "KNeighborsClassifier(n_neighbors=1)"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "knn.fit(X_train, y_train)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 4. 用模型推理(预测)用户的喜好"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "new_user = np.array([[3, 4]])\n",
    "prediction = knn.predict(new_user)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 5. 数据可视化"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAkUAAAHJCAYAAACL5E3/AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguNCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8fJSN1AAAACXBIWXMAAA9hAAAPYQGoP6dpAABXf0lEQVR4nO3deVhUZfsH8O8BhgFkUUQEBcVcwA0VV7TCDTdSyDJftMTUFl+tXNJXy1Q00zQjey0zN6qfZLlRGaao4a6JiqmZK4oLgisI6DDMPL8/jHkdZwZmYBaQ7+e65tJ5znOec5/bR7k9qySEECAiIiKq4uxsHQARERFRRcCiiIiIiAgsioiIiIgAsCgiIiIiAsCiiIiIiAgAiyIiIiIiACyKiIiIiACwKCIiIiICwKKIiIiICACLIiJcvHgRkiRBkqQS+w0fPhySJGHmzJnWCcwMimOWJAlvvPGGwX4KhQKenp6avikpKdYL8h/FsVpy2ykpKZp9LP44ODjAx8cHkZGR+P333y227UepVCpMnz4dDRs2hKOjIyRJwvDhw62ybSIyzMHWARCRdaxduxb//e9/4ejoqLPs119/xZ07d2wQlW3Url0bffr0AQA8ePAAaWlp+Pnnn/HLL7/giy++wOjRoy26/UWLFmH27NmoU6cOBg4cCCcnJzz99NMW3SYRlY5FEVEV0KZNGxw9ehRJSUmIiorSWf5///d/sLe3R4sWLXDs2DHrBwhg7ty5mDJlCurVq2fxbQUFBSE+Pl7zXQiBWbNmYebMmZg4cSJeeOEFeHt7W2z7iYmJAIDdu3fjqaeesth2iMg0PH1GVAVER0fDzs4Oq1ev1ll29+5dJCUloUePHvDx8bFBdA/5+voiKCgILi4uVt+2JEn44IMP0LBhQ9y/fx9bt2616PauXLkCACyIiCoYFkVE5ZSXl4e5c+eiVatW8PDwgKurKxo2bIhBgwZhy5YtOv0LCgowd+5ctGnTBq6urnB1dUWnTp3wzTff6B1fkiQEBASgsLAQs2bNQlBQEORyud4jPob4+vqie/fu2LRpE3JycrSW/fjjj1AoFHj55ZdLHOPy5ct44403UL9+fcjlcnh7e2PgwIE4dOiQVr8jR45AkiR07NjR4Fj//e9/IUkSJkyYoGkr6ZoiU3NWFnZ2dmjVqhWAh/sK/O8apOHDh+P69esYNWoU/Pz84ODggM8++0yz7uXLlzF27Fg0bNgQTk5O8PT0xHPPPYd9+/ZpbaN4H9PT0wFA69qmixcvlnl/jZkjZR1TpVLh448/RpMmTSCXy+Hv74///Oc/UCgUetfLz8/Hxx9/jHbt2sHd3R3VqlVDUFAQxowZgzNnzuj0P3jwIAYNGgRfX184OjrCz88Po0aNQkZGht7xiSyJp8+IykGlUqFnz544ePAgvLy80LVrVzg5OeHKlStISkpCtWrV0Lt3b03/7OxshIeH488//4SPjw/CwsIghMC+ffswfPhwpKam4r///a/OdtRqNaKiorBr1y6EhYUhODgYNWvWNCnWoUOHYtu2bVi/fj1GjBihaV+9ejVcXFzw/PPP6z2SBADHjx9H9+7dcfPmTQQGBmLgwIHIyMjAxo0b8csvvyAhIQGDBg0CAISEhCAoKAh//PEHzp8/j4YNG+qMV7yd0goxoOw5K4t79+4BAORyuVb7jRs30L59exQVFeHpp5/GgwcPNEe09u/fj4iICNy5cweBgYGIiIjAjRs3sGXLFvz2229YvXo1Bg8eDACa64bWrVuH/Px8xMTEaLbh6uparv0taY6UJ4dDhgxBUlISunbtisDAQOzevRvz58/H1atX8X//939afTMzMxEeHo6TJ0+iRo0a6Nq1K+RyOS5cuICvvvoKjRs3RpMmTTT9v/zyS7z11lsAgPbt2+OZZ57B6dOnsWLFCvz888/YuXMnmjZtauSfHpEZCKIqLj09XQAQpf11iImJEQDEjBkzNG07duwQAET79u3F/fv3tfrn5OSI1NRUrbZ+/foJAOKdd94RDx480LRfv35dtGvXTgAQmzdv1lqnOLZGjRqJK1eumLRvxTF/9913Ijc3Vzg7O4tu3bppll+6dElIkiSio6OFEEL07t1bABC///67po9arRYtW7YUAMTkyZOFWq3WLFu3bp2ws7MTrq6u4tq1a5r22bNnCwBi1qxZOjGdO3dOABBBQUF6Y31020KULWeG/P777wKACAsL01mWlZUl3N3dBQCRnJys1R+AeP755/X+Gfv6+gp7e3vxf//3f1rLDh06JGrUqCFcXV1Fdna21rL69esbnG+WmCPlGbNp06YiMzNT037hwgVRvXp1AUCcO3dOa50ePXoIAOKll14S9+7d01qWnp4ujh07pvm+f/9+YW9vL+rWravz92T58uUC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9H8jHBGv9YJfkzo2jo6I6Mlk06IoOzsbw4YNQ2ZmJjw8PBAcHIwtW7YgPDwcAJCRkQE7u/8dzOrcuTMSEhIwbdo0vPfee2jcuDESExPRokULW+0CkcW413TDoHcHIOqdvkhKSsKIj4bysD0RkQXZtChasWJFictTUlJ02gYNGoRBgwZZKCIiIiKqqircNUVEREREtmDykaLHH55YTJIkODk5oVGjRoiMjISnp2e5gyMiIiKyFpOLoqNHj+LIkSNQqVQIDAwEAJw5cwb29vYICgrCl19+iYkTJ2LPnj1me9YQERERkaWZfPosMjISPXv2xLVr13D48GEcPnwYV65cQXh4OKKjo3H16lU8++yzGD9+vCXiJSIiIrIIk4uiBQsWYPbs2VpPg/bw8MDMmTMxf/58uLi4YPr06Th8+LBZAyUiIiKyJJOLopycHGRnZ+u037hxQ/MI7urVq6OwsLD80RERERFZSZlOn40YMQIbN27ElStXcOXKFWzcuBEjR45EVFQUAOCPP/5AkyZNzB0rERERkcWYfKH10qVLMX78ePzrX/9CUVHRw0EcHBATE4O4uDgAQFBQEJYvX27eSImIiIgsyOSiyNXVFcuWLUNcXBwuXLgAAHjqqafg6uqq6dO6dWuzBUhERERkDWV+orWrqyuCg4PNGQsRERGRzZhcFOXn52PevHnYvn07srOzoVartZYXHz0iIiIiqkxMLopGjRqFnTt34pVXXoGvry8kSbJEXERERERWZXJRtHnzZvz666/o0qWLJeIhIiIisgmTb8mvUaMG32tGRERETxyTi6LZs2dj+vTpKCgosEQ8RERERDZh8umzhQsX4vz586hduzYCAgIgk8m0lh85csRswRERERFZi8lFUfFTq4mIiIieJCYXRTNmzLBEHEREREQ2ZfI1RURERERPIqOOFHl6euLMmTPw8vJCjRo1Snw20e3bt80WHBEREZG1GFUUxcXFwc3NTfN7PrCRiIiInjRGFUUxMTGa3w8fPtxSsRARERHZjMnXFNnb2yM7O1un/datW7C3tzdLUERERETWZnJRJITQ265QKODo6FjugIiIiIhswehb8j///HMAgCRJWL58OVxdXTXLVCoVdu3ahaCgIPNHSERERGQFRhdFcXFxAB4eKfrqq6+0TpU5OjoiICAAX331lfkjJCIiIrICo4ui9PR0AEC3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FaIlIiKiJ5lNi6KdO3dizJgxOHDgAJKTk6FUKtGrVy/k5+cbXGffvn2Ijo7GyJEjcfToUURFRSEqKgonTpywYuRERET0pDH69Jkl/Pbbb1rf4+Pj4e3tjcOHD+PZZ5/Vu86iRYvQp08fTJo0CQAwe/ZsJCcnY/Hixfjqq68sHjMRERE9mWxaFD0uJycHAEp8p9r+/fsxYcIErbbevXsjMTFRb3+FQgGFQqH5npubCwBQKpVQKpXljFhb8XjmHvdJxFwZj7kyHnNlPObKNMyX8SyVK2vkXhIV5MVlarUaAwYMwN27d7Fnzx6D/RwdHfHNN98gOjpa0/bll18iNjYWWVlZOv1nzpyJ2NhYnfaEhAS4uLiYJ3giIiKyqIKCAgwZMgQ5OTlwd3e3yDYqzJGiMWPG4MSJEyUWRGUxdepUrSNLubm58Pf3R69evcyeVKVSieTkZISHh0Mmk5l17CcNc2U85sp4zJXxmCvTMF/Gs1Suis/0WFKFKIrGjh2LTZs2YdeuXfDz8yuxr4+Pj84RoaysLPj4+OjtL5fLIZfLddplMpnFJrYlx37SMFfGY66Mx1wZj7kyDfNlPHPnyhp5t+ndZ0IIjB07Fhs3bsSOHTvQoEGDUtcJDQ3F9u3btdqSk5MRGhpqqTCJiIioCrDpkaIxY8YgISEBP/30E9zc3HD9+nUAgIeHB5ydnQE8fJJ23bp1MXfuXADAO++8g7CwMCxcuBARERFYs2YNUlNT8fXXX9tsP4iIiKjys+mRoiVLliAnJwddu3aFr6+v5vPDDz9o+mRkZCAzM1PzvXPnzkhISMDXX3+NVq1aYd26dUhMTESLFi1ssQtERET0hLDpkSJjbnxLSUnRaRs0aBAGDRpkgYiIiIioqrLpkSIiIiKiioJFERERERFYFBEREREBYFFEREREBIBFEREREREAFkVEREREAFgUEREREQFgUUREREQEgEUREREREQAWRUREREQAWBQRERERAWBRRERERASARRERERERABZFRERERABYFBEREREBYFFEREREBIBFEREREREAFkVEREREAFgUEREREQFgUUREREQEgEUREREREQAWRUREREQAWBQRERERAWBRRERERASARRERERERABZFRERERABYFBEREREBYFFEREREBIBFEREREREAFkVEREREAFgUEREREQFgUUREREQEgEUREREREQAWRUREREQAWBQRERERAWBRRERERASARRERERERABZFRERERABYFBEREREBYFFEREREBIBFEREREREAFkVEREREAFgUEREREQFgUUREREQEgEUREREREQAbF0W7du1C//79UadOHUiShMTExBL7p6SkQJIknc/169etEzARERE9sWxaFOXn56NVq1b44osvTFrv9OnTyMzM1Hy8vb0tFCERERFVFQ623Hjfvn3Rt29fk9fz9vZG9erVzR8QERERVVk2LYrKqnXr1lAoFGjRogVmzpyJLl26GOyrUCigUCg033NzcwEASqUSSqXSrHEVj2fucZ9EzJXxmCvjMVfGY65Mw3wZz1K5skbuJSGEsPhWjCBJEjZu3IioqCiDfU6fPo2UlBS0a9cOCoUCy5cvx3fffYeDBw8iJCRE7zozZ85EbGysTntCQgJcXFzMFT4RERFZUEFBAYYMGYKcnBy4u7tbZBuVqijSJywsDPXq1cN3332nd7m+I0X+/v64efOm2ZOqVCqRnJyM8PBwyGQys479pGGujMdcGY+5Mh5zZRrmy3iWylVubi68vLwsWhRVytNnj+rQoQP27NljcLlcLodcLtdpl8lkFpvYlhz7ScNcGY+5Mh5zZTzmyjTMl/HMnStr5L3SP6coLS0Nvr6+tg6DiIiIKjmbHinKy8vDuXPnNN/T09ORlpYGT09P1KtXD1OnTsXVq1fx7bffAgA+++wzNGjQAM2bN8eDBw+wfPly7NixA1u3brXVLhAREdETwqZFUWpqKrp166b5PmHCBABATEwM4uPjkZmZiYyMDM3ywsJCTJw4EVevXoWLiwuCg4Oxbds2rTGIiIiIysKmRVHXrl1R0nXe8fHxWt8nT56MyZMnWzgqIiIiqooq/TVFRERERObAooiIiIgILIqIiIiIALAoIiIiIgLAooiIiIgIAIsiIiIiIgAsioiIiIgAsCgiIiIiAsCiiIiIiAgAiyIiIiIiACyKiIiIiACwKCIiIiICwKKIiIiICACLIiIiIiIALIqIiIiIALAoIiIiIgLAooiIiIgIAIsiIiIiIgAsioiIiIgAsCgiIiIiAsCiiIiIiAgAiyIiIiIiACyKiIiIiACwKCIiIiICwKKIiIiICACLIiIiIiIALIqIiIiIALAoIiIiIgu5desWvL29cfHiRatvu1OnTli/fr1J67Ao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",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.title(\"User Movie Preference\", size=15) \n",
    "plt.xlabel(\"Movie A rating\")\n",
    "plt.ylabel(\"Movie B rating\")\n",
    "plt.grid()\n",
    "\n",
    "# 绘制原始样本点，要求不同的影片喜好类别用不同的颜色标记\n",
    "# 提示: 用scatter散点图绘制，用它的参数c实现不同的类别用不同的颜色标记\n",
    "\n",
    "plt.scatter(X_train[:, 0], X_train[:, 1], c=y_train, cmap='viridis')\n",
    "# 绘制新数据点，用红色x标记，大小为8\n",
    "# 提示：用plt.plot()绘制，用它的参数marker实现不同的标记符号\n",
    "\n",
    "plt.plot(new_user[0, 0], new_user[0, 1], 'rx', markersize=8)\n",
    "# 新数据最近邻索引为第一个最近邻的索引\n",
    "dist, idx = knn.kneighbors(new_user)\n",
    "nearest =X_train[idx[0][0]] # 获取最近邻点的坐标，这是一个列表，第一个元素是x坐标，第二个元素是y坐标\n",
    "\n",
    "# 用红线标记新数据点与最近邻点的连接线\n",
    "# 提示：用plt.plot()绘制，用 r-- 实现红色虚线\n",
    "\n",
    "plt.plot([new_user[0, 0], nearest[0]], [new_user[0, 1], nearest[1]], 'r--')\n",
    "\n",
    "# 为每个点添加坐标文本  \n",
    "for x, y in zip(X_train[:, 0], X_train[:, 1]):\n",
    "    plt.text(x, y+0.1, f'({x}, {y})') \n",
    "\n",
    "# 为新数据点添加坐标文本\n",
    "plt.text(new_user[0,0], new_user[0,1]+0.1, f'({new_user[0,0]}, {new_user[0,1]})')\n",
    "plt.show()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.4"
  },
  "orig_nbformat": 4
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
